﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace CommandLib
{

    /// <summary>
    /// 高性能深度复制
    /// </summary>
    public static class DeepCopy
    {
        private static Dictionary<Type, Delegate> _cachedIl { set; get; } = new Dictionary<Type, Delegate>();
        private static Dictionary<Type, Delegate> CachedIl { get; set; } = new Dictionary<Type, Delegate>();
        /// <summary>
        /// 高效深度复制对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="myObject"></param>
        /// <returns></returns>
        public static T ToDeepCopy<T>(this T myObject) where T : class
        {
            Delegate myExec = null;
            if (!_cachedIl.TryGetValue(typeof(T), out myExec))
            {
                DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T), new Type[] { typeof(T) }, true);
                ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
                ILGenerator generator = dymMethod.GetILGenerator();
                LocalBuilder lbf = generator.DeclareLocal(typeof(T));
                generator.Emit(OpCodes.Newobj, cInfo);
                generator.Emit(OpCodes.Stloc_0);
                foreach (FieldInfo field in myObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    generator.Emit(OpCodes.Ldloc_0);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldfld, field);
                    generator.Emit(OpCodes.Stfld, field);
                }
                generator.Emit(OpCodes.Ldloc_0);
                generator.Emit(OpCodes.Ret);
                myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
                _cachedIl.Add(typeof(T), myExec);
            }
            return ((Func<T, T>)myExec)(myObject);
        }
        /// <summary>
        /// 属性字段值复制
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">源对象</param>
        /// <param name="model">目标对象</param>
        public static void CloneField<T>(this T source, T model)
        {

            if (CachedIl.ContainsKey(typeof(T)))
            {
                ((Action<T, T>)CachedIl[typeof(T)])(source, model);
                return;
            }
            var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
            ILGenerator generator = dynamicMethod.GetILGenerator();

            foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
            {
                //不复制静态类属性
                if (temp.GetAccessors(true)[0].IsStatic)
                {
                    continue;
                }
                generator.Emit(OpCodes.Ldarg_1);
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Callvirt, temp.GetMethod);
                generator.Emit(OpCodes.Callvirt, temp.SetMethod);
            }
            generator.Emit(OpCodes.Ret);
            var clone = (Action<T, T>)dynamicMethod.CreateDelegate(typeof(Action<T, T>));
            CachedIl[typeof(T)] = clone;
            clone(source, model);
        }
        /// <summary>
        /// 稳定的常规深度复制
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Value">源对象</param>
        /// <returns>返回新对象</returns>
        public static T DeepCopyObject<T>(this T Value) where T : class
        {
            var result = default(T);
            if (Value != null)
            {
                result = Value.ToJsonString().JsonToObject<T>();
            }

            return result;
        }

    }


}
